#include "GameCore.h"
#include "base\Input.h"
#include <vector>
#include "utils\Log.h"
#include "game\World.h"
#include "renderer\DebugRenderer.h"
#include "GameEventData.h"
#include "Constants.h"
#include "SimpleHighscoreService.h"
#include <particles\ParticleSystem.h>
#include "GameActions.h"
#include "src\static\Gutter.h"

AstroGame::AstroGame() {

	m_Sprites = new ds::DynamicSpriteNode("Core","BallzMaterial");
	gEngine->getWorld().attachNode(m_Sprites,"Background");
	m_Player = new Ship();	
	gEngine->getWorld().attachNode(m_Player,"Background");

	Gutter* m_Gutter = new Gutter();
	gEngine->getWorld().attachNode(m_Gutter,"Background");
	//
	m_Bullets = new BulletSystem(m_Sprites);

	m_ParticleManager = static_cast<ds::ParticleManager*>(gEngine->getWorld().getNode("Particles"));
	// HUD
	m_HUD = new NewHUD();
	gEngine->getWorld().attachNode(m_HUD,"HUD");
	
	m_EnemySprites = new ds::DynamicSpriteNode("Enemies","TexturesMaterial");
	//m_EnemySprites->setActive(false);
	gEngine->getWorld().attachNode(m_EnemySprites,"Background");
	m_Particles = new FloatingParticles(m_EnemySprites);
	m_Meteoroids = new Meteoroids(m_EnemySprites);
	m_Asteroids = new Asteroids(m_EnemySprites);

	m_Swimmer = new Swimmer();
	m_Swimmer->setActive(false);
	gEngine->getWorld().attachNode(m_Swimmer,"Background");

	m_SpriteNode = new ds::SpriteNode("BasicSprites","TexturesMaterial",ds::SCM_MODULATE);
	gEngine->getWorld().attachNode(m_SpriteNode,"Base");
	m_LevelInfo = new ds::SpriteCounter(m_SpriteNode,ds::Rect(348,0,86,65),86,2);
	m_LevelInfo->setPos(ds::Vec2(430,336));
	m_LevelInfo->setCounter(1);
	m_LevelInfo->setScale(ds::Vec2(2.0f,2.0f));
	m_LevelInfo->setColor(ds::Color(0.9f,0.9f,0.9f,1.0f));

	m_Level = 1;
}

AstroGame::~AstroGame() {
	delete m_Bullets;
}

GameCore::GameCore() {
	gEngine->getEventManager().registerEventHandler(this);
	gEngine->getCollisionDetector().registerHandler(this);
	//m_Dispose = false;
	//m_DisposeTimer = 0.0f;
	//m_ShotTimer = 0.0f;
	m_AutoFire = true;
	m_Magnetic = false;
	
}


GameCore::~GameCore(void) {
}

void GameCore::loadContent(ds::ResManager& resourceManager) {	
	m_Engine = new AstroGame();	
	ds::ActionManager& am = gEngine->getActionManager();
	am.addAction(new LevelInfoAction(m_Engine));
	am.addAction(new ClearAction(m_Engine));
	am.addAction(new SwimmerAction(m_Engine));
	am.addAction(new BackgroundAction(m_Engine));
	group = new ds::ActionGroup("RunGame");
	group->addAction(new ShootAction(m_Engine));
	group->addAction(new SetTargetsAction(m_Engine));
	group->addAction(new UpdateEnemiesAction(m_Engine));
	am.addAction(new BlitzAction(m_Engine));
	am.addAction(new FlashAction(m_Engine));
	am.addAction(new GrayFadeAction(m_Engine));
	am.addAction(new PowerupInfoAction(m_Engine,"HealthBonus",m_Engine->basicSprites(),ds::Rect(915,500,490,62)));
	am.addAction(new PowerupInfoAction(m_Engine,"DoubleLaser",m_Engine->basicSprites(),ds::Rect(915,0,490,62)));
	am.addAction(new PowerupInfoAction(m_Engine,"TripleLaser",m_Engine->basicSprites(),ds::Rect(660,410,460,62)));
	am.addAction(group);
	am.startAction("Background");
}

void GameCore::start() {
	m_Engine->bullets().setFireMode(FM_SINGLE);
	setMode(GM_LEVEL_INFO);
}

void GameCore::onUIEvent(const ds::IUIEvent& event) {
	if ( event.getEventType() == ds::UI_EVENT_BUTTON_CLICKED ) {
		if (event.getControlID() == 12 ) {
			start();
		}			
		if ( event.getControlID() == 13 ) {
			gEngine->activateGameState("Start");
		}			
	}
}

void GameCore::setMode(GameMode mode) {
	m_Mode = mode;
	if ( mode == GM_LEVEL_INFO ) {
		gEngine->getActionManager().stopAction("RunGame");	
		gEngine->getActionManager().startAction("ClearActors");	
		gEngine->getActionManager().startAction("LevelInfo");		
	}
	else if ( mode == GM_RUNNING ) {
		m_Engine->enemies()->setActive(true);
		m_Engine->swimmer().setActive(true);
		m_Engine->player().setBlocked(false);
		gEngine->getActionManager().startAction("RunGame");	
		startWave();
	}
	else if ( mode == GM_OVER ) {
		gEngine->getActionManager().startAction("ClearActors");	
		gEngine->getActionManager().stopAction("RunGame");	
		SimpleHighscoreService* shs = static_cast<SimpleHighscoreService*>(gEngine->getService("Highscore"));
		shs->setScore(m_Engine->hud().getPoints());
		if ( shs->isNewHighscore() ) {
			shs->saveHighscore();
		}
	}
}

// ------------------------------------------------------------
//
// ------------------------------------------------------------
void GameCore::usePowerup(const char* name) {
	gEngine->getActionManager().startAction(name);
}

void GameCore::startWave() {	
	m_Engine->asteroids().startWave((m_Engine->getLevel()));
	m_Engine->meteoroids().start(m_Engine->getLevel(),10.0f);
}
// ------------------------------------------------------------
// Handling all kind of events here
// ------------------------------------------------------------
void GameCore::process(ds::EventBuffer& buffer ) {
	if ( buffer.containsID(EVN_BOMB_EXPLODED) ) {
		BombExplosionEvent ded;
		buffer.getByID(&ded,sizeof(BombExplosionEvent),EVN_BOMB_EXPLODED);
		//m_ParticleSystem->start(ded.position,0);
		m_Engine->particles()->start(ds::Vec3(ded.position.x,ded.position.y,0.0f),"ImpactExplosion");
	}
	if ( buffer.containsID(EVN_ASTEROID_DESTROYED) ) {
		AsteroidEvent ded;
		buffer.getByID(&ded,sizeof(AsteroidEvent),EVN_ASTEROID_DESTROYED);
		int type = ded.type;
		float radius = (float)ded.size/2.0f;
		int counter = 10 + type * 10;
		ds::Vec2 pos = ded.position;
		startRingExplosion(ded.position,radius);
		m_Engine->hud().addScore(500);
		gEngine->getAudioManager().play("Implosion",100,false);
		int cnt = 4 + type * 3;
		m_Engine->floatingParticles().add(pos,cnt,radius/2.0f,radius);
	}
	if ( buffer.containsID(EVN_LEVEL_FINISHED) ) {	
		//m_LevelInfo->setCounter(m_Level);
		m_Engine->incLevel();
		m_Engine->hud().addScore(1000);
		m_Engine->hud().setLevel(m_Engine->getLevel());	
		setMode(GM_LEVEL_INFO);
	}
	if ( buffer.containsID(EVN_START_WAVE) ) {
		setMode(GM_RUNNING);
	}
}

void GameCore::startRingExplosion(const ds::Vec2& pos,float radius) {
	ds::Vec3 center = ds::Vec3(pos.x+radius,pos.y+radius,0.0f);	
	ds::ParticleSystem* reps = m_Engine->particles()->prepare("RingExplosion");
	ds::RingEmitter* re = static_cast<ds::RingEmitter*>(reps->getEmitter());
	re->setRadius(radius);
	m_Engine->particles()->start(center,reps);


	reps = m_Engine->particles()->prepare("DebrisExplosion");
	re = static_cast<ds::RingEmitter*>(reps->getEmitter());
	re->setRadius(radius);
	m_Engine->particles()->start(center,reps);
	
}
// ------------------------------------------------------------
// collision callback handler
// ------------------------------------------------------------
void GameCore::processBuffer(ds::CollisionBuffer& buffer) {
	if ( m_Mode == GM_RUNNING ) {
		ds::CollisionEvent col;
		for ( uint32 x = 0; x < buffer.size(); ++x ) {
			buffer.get(x,&col);
			if ( ds::collision::isBetween(col,BULLET_ID,ASTEROID_ID) ) {
				gEngine->getAudioManager().play("SmallHit",20,false);
				ds::Vec2 pp = ds::collision::getPositionByID(col,BULLET_ID);
				m_Engine->particles()->start(ds::Vec3(pp.x,pp.y,0.0f),"ImpactExplosion");				
				m_Engine->bullets().remove(ds::collision::getHandleByID(col,BULLET_ID));
				m_Engine->asteroids().killAsteroid(ds::collision::getHandleByID(col,ASTEROID_ID));
				m_Engine->hud().addScore(100);
			}
			else if ( ds::collision::isBetween(col,BULLET_ID,METEOROID_ID) ) {
				gEngine->getAudioManager().play("SmallHit",20,false);
				ds::Vec2 pp = ds::collision::getPositionByID(col,METEOROID_ID);
				m_Engine->particles()->start(ds::Vec3(pp.x,pp.y,0.0f),"ImpactExplosion");		
				m_Engine->meteoroids().hit(ds::collision::getHandleByID(col,METEOROID_ID));
				gEngine->getAudioManager().play("Explosion",100,false);
				m_Engine->bullets().remove(ds::collision::getHandleByID(col,BULLET_ID));
				m_Engine->hud().addScore(500);
			}
			else if ( ds::collision::isBetween(col,SHIP_ID,METEOROID_ID) ) {
				ds::Vec2 pp = ds::collision::getPositionByID(col,SHIP_ID);
				m_Engine->particles()->start(ds::Vec3(pp.x,pp.y,0.0f),"ImpactExplosion");		
				m_Engine->meteoroids().clear();
				gEngine->getAudioManager().play("Explosion",100,false);
				m_Engine->hud().decHealth(METEOROID_IMPACT);			
			}
			/*
			else if ( ds::collision::isBetween(col,SHIP_ID,HEALTH_PU_ID) ) {								
				gEngine->getAudioManager().play("Powerup",100,false);
				m_Engine->hud().incHealth(HEALTH_INCREASE);		
				m_Engine->powerups().removeByHandle(ds::collision::getHandleByID(col,HEALTH_PU_ID));
			}
			else if ( ds::collision::isBetween(col,SHIP_ID,DP_LASER_PU_ID) ) {								
				gEngine->getAudioManager().play("Powerup",100,false);
				m_Engine->bullets().setFireMode(FM_DOUBLE);
				m_Engine->powerups().removeByHandle(ds::collision::getHandleByID(col,DP_LASER_PU_ID));
			}
			*/
			else if ( ds::collision::isBetween(col,BULLET_ID,SWIMMER_ID) ) {	
				m_Engine->bullets().remove(ds::collision::getHandleByID(col,BULLET_ID));
				//gEngine->getAudioManager().play("Explosion",100,false);
				int energy = m_Engine->swimmer().handleHit(ds::collision::getHandleByID(col,SWIMMER_ID));
				gEngine->getAudioManager().play("SmallHit",20,false);				
				if ( energy == 0 ) {
					m_Engine->hud().addScore(500);
					m_Engine->swimmer().removeByHandle(ds::collision::getHandleByID(col,SWIMMER_ID));
					ds::Vec2 pp = ds::collision::getPositionByID(col,METEOROID_ID);
					m_Engine->particles()->start(ds::Vec3(pp.x,pp.y,0.0f),"ImpactExplosion");		
				}	
			}
			else if ( ds::collision::isBetween(col,SHIP_ID,SWIMMER_ID) ) {								
				gEngine->getAudioManager().play("Explosion",100,false);
				m_Engine->swimmer().removeByHandle(ds::collision::getHandleByID(col,SWIMMER_ID));
				//m_Bullets->setFireMode(FM_DOUBLE);
				if ( m_Engine->hud().decHealth(20) <= 0 ) {
					FIRE_EMPTY_EVENT(EVN_GAME_OVER);				
					setMode(GM_OVER);
				}	
			}
			/*
			else if ( ds::collision::isBetween(col,SHIP_ID,TP_LASER_PU_ID) ) {								
				gEngine->getAudioManager().play("Powerup",100,false);
				m_Engine->bullets().setFireMode(FM_TRIPLE);	
				m_Engine->powerups().removeByHandle(ds::collision::getHandleByID(col,TP_LASER_PU_ID));
			}
			*/
			else if ( ds::collision::isBetween(col,ASTEROID_ID,SHIP_ID) ) {
				//if ( !m_Dispose ) {
					//m_Dispose = true;
					//m_DisposeTimer = 0.0f;
					Asteroid& asteroid = m_Engine->asteroids().getAsteroid(ds::collision::getHandleByID(col,ASTEROID_ID));
					LOG(logINFO) << "ship hit by asteroid " << asteroid.type;
					int impact = IMPACTS[asteroid.type];					
					gEngine->getAudioManager().play("Explosion",100,false);
					m_Engine->asteroids().killAsteroid(ds::collision::getHandleByID(col,ASTEROID_ID));
					if ( m_Engine->hud().decHealth(impact) <= 0 ) {
						FIRE_EMPTY_EVENT(EVN_GAME_OVER);				
						setMode(GM_OVER);
					}			
				//}
			}	
		}
	}
}
